﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using SAServices.Model;
using System.IO;
using System.Security.Principal;
using System.Diagnostics;

namespace SAServices.RemtInvtor
{
    public interface InvtRmtServiceInterface
    {
          bool IsInventorReady();

         List<ParameterInfo> GetParameters(string modelPath);

         bool GeneratePart(string templatePath, List<ParameterInfo> driveParams, string targetPath);

         bool DeleteCache(Guid familyId);

         bool GenerateCache(string templatePath, List<ParameterInfo> driveParams, List<string> cachePaths);

         string SayHello(string who);
    }


    public class InvtRmtService : MarshalByRefObject, InvtRmtServiceInterface
    {
        public static string lastErrMsg;
        private static string INVOBJ = "Inventor.Application";
        private static Inventor.Application m_InvApp;

        public InvtRmtService(){
            //IsInventorReady();
        }

        public string SayHello(string who)
        {
            return who + "say:hello world!";
        }

        private static void ClearInventorProcess()
        {
            System.Diagnostics.Process[] inventors = System.Diagnostics.Process.GetProcessesByName(@"Inventor");
            foreach (System.Diagnostics.Process inv in inventors)
            {
                inv.Kill();
            }

            m_InvApp = null;
        }

        public  bool IsInventorReady()
        {
            Inventor.Application app =GetInventorApp();
            bool bRet = app.Ready;
            return bRet;
        }

        public static Inventor.Application GetInventorApp()
        {
            //try to get if not exist.
            if (m_InvApp == null)
            {
                try
                {   //try to get the object.

                    string user= WindowsIdentity.GetCurrent().Name;
 
// impersonate - unneccessary for form application
 //if (impersonateValidUser("Administrator", "MyComputer", "passWord"))
 //{
 // just for kicks, check who we are now...
 // should be the impersonated user
 string impostor = WindowsIdentity.GetCurrent().Name;
 // check if tstApp is running
 System.Diagnostics.Process[] processes;
 //processes = Process.GetProcessesByName("tstApp32");
 //// check if the executable is running
 //if (processes.Length == 0)
 //{
 //zVal.zenError += "ZRT: tstApp32 is NOT running !! ";
 //}
 //else
 //{
 //zVal.zenError += "ZRT: tstApp32 is running !! ";
 // the test application is found, get the handle !!
 // the permissions have to be in order...
 System.Security.PermissionSet ps = new
 System.Security.PermissionSet
 (System.Security.Permissions.
 PermissionState.None);
 ps.AddPermission(new
 System.Security.Permissions.SecurityPermission
 (System.Security.Permissions.
 
SecurityPermissionFlag.UnmanagedCode));
 ps.Assert();
 
// the permissions should now be in order.... get the handle !!!
m_InvApp = (Inventor.Application)Marshal.GetActiveObject(INVOBJ);
 //}
 

                    
                }
                catch (System.Runtime.InteropServices.COMException ex)
                {
                    lastErrMsg = ex.Message;
                    File.AppendAllText(@"E:\GlobalModules\01_WCFService\11.log", "COMException invApp=" + lastErrMsg);
                    m_InvApp = null;
                }
                catch (Exception ex)
                {
                    lastErrMsg = ex.Message;
                    File.AppendAllText(@"E:\GlobalModules\01_WCFService\11.log", "Exception invApp=" + lastErrMsg);
                    m_InvApp = null;
                }
            }
            else
            {
                try
                {
                    bool bReady = m_InvApp.Ready;

                    if (!bReady)
                    {
                        ClearInventorProcess();
                    }
                }
                catch (System.Exception)
                {
                    ClearInventorProcess();
                }
            }

            //try to create if not exist.
            if (m_InvApp == null)
            {
                try
                {
                    System.Type InvType = System.Type.GetTypeFromProgID(INVOBJ);
                    m_InvApp = (Inventor.Application)System.Activator.CreateInstance(InvType);
                }
                catch (Exception ex)
                {
                    lastErrMsg = ex.Message;
                    m_InvApp = null;
                }
            }

            return m_InvApp;
        }

        public List<ParameterInfo> GetParameters(string modelPath)
        {
            List<ParameterInfo> retParams = new List<ParameterInfo>();

            //todo: get inventor instance.
            Inventor.Application invApp = GetInventorApp();

            //File.AppendAllText(@"E:\GlobalModules\01_WCFService\11.log", "invApp=" + invApp);

            if (invApp == null)
            {
                return null;
            }

            //if this is part document
            Inventor.PartDocument curDoc = null;

            try
            {
                if (invApp.Ready)
                {
                    invApp.Documents.CloseAll();
                    curDoc = (Inventor.PartDocument)invApp.Documents.Open(modelPath);
                }

                //get user parameters.
                System.Collections.IEnumerator enumer = curDoc.ComponentDefinition.Parameters.UserParameters.GetEnumerator();
                int i = 0;

                while (enumer.MoveNext())
                {
                    Inventor.UserParameter invParam = (Inventor.UserParameter)enumer.Current;
                    ParameterInfo newParam = new ParameterInfo();
                    newParam.ID = Guid.NewGuid();
                    newParam.Name = invParam.Name;
                    newParam.Unit = invParam.get_Units();
                    newParam.Value = Convert.ToString(invParam.Value);
                    newParam.IsDriver = true;
                    //reference key.
                    Array arrRefKey = new Byte[1];
                    invParam.GetReferenceKey(ref arrRefKey);
                    newParam.RefKey = curDoc.ReferenceKeyManager.KeyToString(arrRefKey);
                    newParam.Idx = i++;

                    retParams.Add(newParam);
                }

                //don't need the model parameters.
                int cnt = curDoc.ComponentDefinition.Parameters.ModelParameters.Count;

                enumer = curDoc.ComponentDefinition.Parameters.ModelParameters.GetEnumerator();

                while (enumer.MoveNext())
                {
                    Inventor.ModelParameter invParam = (Inventor.ModelParameter)enumer.Current;
                    ParameterInfo newParam = new ParameterInfo();
                    newParam.ID = Guid.NewGuid();
                    newParam.Name = invParam.Name;
                    newParam.Unit = invParam.get_Units();
                    newParam.Value = Convert.ToString(invParam.Value);
                    newParam.IsDriver = true;
                    //reference key.
                    Array arrRefKey = new Byte[1];
                    invParam.GetReferenceKey(ref arrRefKey);
                    newParam.RefKey = curDoc.ReferenceKeyManager.KeyToString(arrRefKey);
                    newParam.Idx = i++;

                    retParams.Add(newParam);
                }

                //fill the result list.

            }
            catch (Exception ex)
            {
                lastErrMsg = ex.Message;
                File.AppendAllText(@"E:\GlobalModules\01_WCFService\11.log", "invApp=" + lastErrMsg);
                return null;
            }

            return retParams;
        }

        public bool GeneratePart(string templatePath, List<ParameterInfo> driveParams, string targetPath)
        {
            //todo: get inventor instance.
            Inventor.Application invApp = GetInventorApp();

            if (invApp == null)
            {
                return false;
            }

            //if this is part document
            Inventor.PartDocument curDoc = null;

            try
            {
                if (invApp.Ready)
                {
                    invApp.Documents.CloseAll();
                    curDoc = (Inventor.PartDocument)invApp.Documents.Open(templatePath);
                }

                foreach (ParameterInfo item in driveParams)
                {
                    //run into issue when use reference key. 
                    //if (String.IsNullOrEmpty(item.ProRefKey))
                    //{
                    //    Array arrRefKey = new Byte[1];
                    //    curDoc.ReferenceKeyManager.StringToKey(item.RefKey,ref arrRefKey);
                    //    Object matchType;
                    //    Inventor.Parameter invParameter = (Inventor.Parameter)curDoc.ReferenceKeyManager.BindKeyToObject(arrRefKey, 0, out matchType);
                    //    invParameter.Value = item.Value;
                    //}
                    bool bAssigned = false;
                    System.Collections.IEnumerator enumer = curDoc.ComponentDefinition.Parameters.UserParameters.GetEnumerator();

                    while (enumer.MoveNext())
                    {
                        Inventor.UserParameter invParam = (Inventor.UserParameter)enumer.Current;
                        if (invParam.Name == item.Name)
                        {
                            try
                            {
                                invParam.Expression = item.Value;
                                bAssigned = true;
                            }
                            catch (Exception ex)
                            {
                                lastErrMsg = ex.Message;
                                break;
                            }

                            break;
                        }
                    }

                    enumer = curDoc.ComponentDefinition.Parameters.ModelParameters.GetEnumerator();

                    while (!bAssigned && enumer.MoveNext())
                    {
                        Inventor.ModelParameter invParam = (Inventor.ModelParameter)enumer.Current;
                        if (invParam.Name == item.Name)
                        {
                            try
                            {
                                invParam.Expression = item.Value;
                                bAssigned = true;
                            }
                            catch (Exception ex)
                            {
                                lastErrMsg = ex.Message;
                                break;
                            }

                            break;
                        }
                    }
                }

                if (!CreateDirByPathIfNotExist(targetPath))
                {
                    lastErrMsg = String.Format(@"服务器无法创建目录{0}", targetPath);
                    return false;
                }

                //save the result.
                invApp.SilentOperation = true;
                curDoc.SaveAs(targetPath, true);
                curDoc.Close();
                invApp.SilentOperation = false;

            }
            catch (Exception ex)
            {
                lastErrMsg = ex.Message;
                return false;
            }

            return true;
        }

        public bool DeleteCache(Guid familyId)
        {
            throw new NotImplementedException();
        }

        public bool GenerateCache(string templatePath, List<ParameterInfo> driveParams, List<string> cachePaths)
        {
            //todo: get inventor instance.
            Inventor.Application invApp = GetInventorApp();

            if (invApp == null)
            {
                return false;
            }

            //if this is part document
            Inventor.PartDocument curDoc = null;

            try
            {
                if (invApp.Ready)
                {
                    invApp.Documents.CloseAll();
                    curDoc = (Inventor.PartDocument)invApp.Documents.Open(templatePath);
                }

                foreach (ParameterInfo item in driveParams)
                {
                    System.Collections.IEnumerator enumer = curDoc.ComponentDefinition.Parameters.UserParameters.GetEnumerator();

                    while (enumer.MoveNext())
                    {
                        Inventor.UserParameter invParam = (Inventor.UserParameter)enumer.Current;
                        if (invParam.Name == item.Name)
                        {
                            try
                            {
                                invParam.Expression = item.Value;
                            }
                            catch (Exception ex)
                            {
                                lastErrMsg = ex.Message;
                                break;
                            }

                            break;
                        }
                    }
                }
                //curDoc.Update();
                //save the result.
                invApp.SilentOperation = true;

                foreach (string path in cachePaths)
                {
                    if (!CreateDirByPathIfNotExist(path))
                        return false;

                    curDoc.SaveAs(path, true);
                }

                curDoc.Close();
                invApp.SilentOperation = false;

            }
            catch (Exception ex)
            {
                lastErrMsg = ex.Message;
                return false;
            }

            return true;
        }

        private static bool CreateDirByPathIfNotExist(string path)
        {
            try
            {
                string dir = System.IO.Path.GetDirectoryName(path);
                if (!System.IO.Directory.Exists(dir))
                {
                    System.IO.Directory.CreateDirectory(dir);
                }
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }
    }
}